import type { StepHandler } from '.';
import { unzipFile } from '@wp-playground/common';
import { logger } from '@php-wasm/logger';
import { Semaphore } from '@php-wasm/util';
/**
 * @inheritDoc setSiteLanguage
 * @hasRunnableExample
 * @example
 *
 * <code>
 * {
 * 		"step": "setSiteLanguage",
 * 		"language": "en_US"
 * }
 * </code>
 */
export interface SetSiteLanguageStep {
	step: 'setSiteLanguage';
	/** The language to set, e.g. 'en_US' */
	language: string;
}

/**
 * Get the translation package URL for a given WordPress version and language.
 * The translation package URL is fetched from the WordPress.org API based on
 * the provided WordPress version.
 *
 * If the translation package is not found, an error is thrown.
 */
export const getWordPressTranslationUrl = async (
	wpVersion: string,
	language: string
): Promise<string> => {
	const languageTranslations = await fetch(
		`https://api.wordpress.org/translations/core/1.0/?version=${wpVersion}`
	);
	const languageTranslationsJson = await languageTranslations.json();
	const languageTranslation = languageTranslationsJson.translations.find(
		(translation: any) =>
			translation.language.toLowerCase() === language.toLowerCase()
	);

	if (!languageTranslation) {
		throw new Error(
			`Failed to get ${language} translation package for WordPress ${wpVersion}.`
		);
	}

	return languageTranslation.package;
};

/**
 * Sets the site language and download translations.
 */
export const setSiteLanguage: StepHandler<SetSiteLanguageStep> = async (
	playground,
	{ language },
	progress
) => {
	progress?.tracker.setCaption(progress?.initialCaption || 'Translating');

	await playground.defineConstant('WPLANG', language);

	const docroot = await playground.documentRoot;

	const wpVersion = (
		await playground.run({
			code: `<?php
			require '${docroot}/wp-includes/version.php';
			echo $wp_version;
		`,
		})
	).text;

	const translations = [
		{
			url: await getWordPressTranslationUrl(wpVersion, language),
			type: 'core',
		},
	];

	const pluginListResponse = await playground.run({
		code: `<?php
		require_once('${docroot}/wp-load.php');
		require_once('${docroot}/wp-admin/includes/plugin.php');
		echo json_encode(
			array_values(
				array_map(
					function($plugin) {
						return [
							'slug'    => $plugin['TextDomain'],
							'version' => $plugin['Version']
						];
					},
					array_filter(
						get_plugins(),
						function($plugin) {
							return !empty($plugin['TextDomain']);
						}
					)
				)
			)
		);`,
	});

	const plugins = pluginListResponse.json;
	for (const { slug, version } of plugins) {
		translations.push({
			url: `https://downloads.wordpress.org/translation/plugin/${slug}/${version}/${language}.zip`,
			type: 'plugin',
		});
	}

	const themeListResponse = await playground.run({
		code: `<?php
		require_once('${docroot}/wp-load.php');
		require_once('${docroot}/wp-admin/includes/theme.php');
		echo json_encode(
			array_values(
				array_map(
					function($theme) {
						return [
							'slug'    => $theme->get('TextDomain'),
							'version' => $theme->get('Version')
						];
					},
					wp_get_themes()
				)
			)
		);`,
	});

	const themes = themeListResponse.json;
	for (const { slug, version } of themes) {
		translations.push({
			url: `https://downloads.wordpress.org/translation/theme/${slug}/${version}/${language}.zip`,
			type: 'theme',
		});
	}

	if (!(await playground.isDir(`${docroot}/wp-content/languages/plugins`))) {
		await playground.mkdir(`${docroot}/wp-content/languages/plugins`);
	}
	if (!(await playground.isDir(`${docroot}/wp-content/languages/themes`))) {
		await playground.mkdir(`${docroot}/wp-content/languages/themes`);
	}

	// Fetch translations in parallel
	const fetchQueue = new Semaphore({ concurrency: 5 });
	const translationsQueue = translations.map(({ url, type }) =>
		fetchQueue.run(async () => {
			try {
				const response = await fetch(url);
				if (!response.ok) {
					throw new Error(
						`Failed to download translations for ${type}: ${response.statusText}`
					);
				}

				let destination = `${docroot}/wp-content/languages`;
				if (type === 'plugin') {
					destination += '/plugins';
				} else if (type === 'theme') {
					destination += '/themes';
				}

				await unzipFile(
					playground,
					new File(
						[await response.blob()],
						`${language}-${type}.zip`
					),
					destination
				);
			} catch (error) {
				/**
				 * Throw an error when a core translation isn't found.
				 *
				 * The language slug used in the Blueprint is not recognized by the
				 * WordPress.org API and will always return a 404. This is likely
				 * unintentional – perhaps a typo or the API consumer guessed the
				 * slug wrong.
				 *
				 * The least we can do is communicate the problem.
				 */
				if (type === 'core') {
					throw new Error(
						`Failed to download translations for WordPress. Please check if the language code ${language} is correct. You can find all available languages and translations on https://translate.wordpress.org/.`
					);
				}
				/**
				 * WordPress core has translations for the requested language,
				 * but one of the installed plugins or themes doesn't.
				 *
				 * This is fine. Not all plugins and themes have translations for
				 * every language. Let's just log a warning and move on.
				 */
				logger.warn(
					`Error downloading translations for ${type}: ${error}`
				);
			}
		})
	);
	await Promise.all(translationsQueue);
};
